﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MealBooking.Core.Services.Interface.OrderService;
using MealBooking.Core.Models.Meals;
using System.IO;
using System.Xml.Serialization;
using System.Xml.Linq;
using MealBooking.Core.Models.Accounts;
using MealBooking.Core.Models.Orders;
using MealBooking.Core.Common;

namespace MealBooking.Core.Services.Impl.OrderService
{
    public class OrderService : IOrderService
    {
        #region private
        static string OrderFolderPath = Path.Combine(Utils.Utils.StorageLocation(), "Orders");
        static string OrderFilePath = Path.Combine(OrderFolderPath, "Orders.xml");

        private static object _orderLocker = new object();

        private static XDocument LoadOrderFile()
        {
            return Utils.Utils.InitXmlFile(OrderFilePath);
        }
        private static void SaveOrderFile(XDocument doc)
        {
            Utils.Utils.SaveFile(_orderLocker, doc, OrderFilePath);
        }
        #endregion

        public List<Order> TodayOrders()
        {
            var doc = LoadOrderFile();
            var xmlTodayOrders = doc.Root.Elements("Order").Where(o =>
                DateTime.Now.Date == ((DateTime)o.Element("CreatedTime")).Date);
            return ModelSerializer.Deserialize<Order>(xmlTodayOrders);
        }
        public Order OrderDetail(string orderId)
        {
            var doc = LoadOrderFile();
            var xmlOrder = doc.Root.Elements("Order").SingleOrDefault(o =>
                orderId.Equals(o.Element("Id").Value));
            return ModelSerializer.Deserialize<Order>(xmlOrder);
        }

        #region User's Order
        public List<Order> Orders(string accountId)
        {
            var doc = LoadOrderFile();
            var xmlOrders = doc.Root.Elements("Order").Where(o =>
                accountId.Equals(o.Element("Account").Element("Id").Value));
            return ModelSerializer.Deserialize<Order>(xmlOrders);
        }
        public Order UserTodayOrder(string accountId)
        {
            var doc = LoadOrderFile();
            var xmlOrder = doc.Root.Elements("Order").SingleOrDefault(o =>
                accountId.Equals(o.Element("Account").Element("Id").Value)
                && (DateTime.Now.Date == ((DateTime)o.Element("CreatedTime")).Date));
            return ModelSerializer.Deserialize<Order>(xmlOrder);
        }
        public Order AddMealToOrder(string customerId, MealWithSum newMeal)
        {
            var account = CoreService.AccountService.AccountDetail(new Account { Id = customerId });
            if (!account.IsActived)
            {
                return null;
            }
            //TODO: manager:
            var manager = CoreService.AccountService.AllManagerInformations().First();
            var userTodayOrder = UserTodayOrder(account.Id);
            if (userTodayOrder == null)//new Order:
            {
                userTodayOrder = new Order
                {
                    Id = Guid.NewGuid().ToString().ToLower(),
                    Account = new Account { Id = account.Id, Name = account.Name },
                    Manager = new Manager { Id = manager.Id, Name = manager.Name },
                    CreatedTime = DateTime.Now,
                    IsBooked = false,
                    MealsWithSum = new List<MealWithSum> { newMeal },
                };
            }
            else //add meals to existedOrder: 
            {
                bool hasAdded = false;
                foreach (var existedMeal in userTodayOrder.MealsWithSum)
                {//合并相同的meal:
                    if (existedMeal.Meal.Equals(newMeal.Meal))
                    {
                        existedMeal.MealSum += newMeal.MealSum;
                        hasAdded = true;
                        break;
                    }
                }
                if (!hasAdded)
                {
                    userTodayOrder.MealsWithSum.Add(newMeal);
                }
            }
            //save to file:            
            return UpdateUserTodayOrder(userTodayOrder);
        }
        public Order UpdateUserTodayOrder(Order newOrder)
        {
            XDocument doc = LoadOrderFile();
            var xmlOrder = doc.Root.Elements("Order").SingleOrDefault(o =>
                newOrder.Id.Equals(o.Element("Id").Value));
            if (xmlOrder != null)
            {
                xmlOrder.Remove();
            }
            var newMealsWithSum = newOrder.MealsWithSum;
            if (newMealsWithSum != null && newMealsWithSum.Count != 0)
            {
                var meals = CoreService.MealService.MealDetails(newMealsWithSum.Select(m => m.Meal.Id).ToList());
                var mealsWithSum =
                    from mealWithSum in newMealsWithSum
                    from meal in meals
                    where mealWithSum.Meal.Id.Equals(meal.Id)
                    select new MealWithSum { Meal = meal, MealSum = mealWithSum.MealSum };
                newOrder.MealsWithSum = mealsWithSum.ToList();
            }
            newOrder.LastUpdateTime = DateTime.Now;
            xmlOrder = ModelSerializer.Serialize(newOrder);
            doc.Root.Add(xmlOrder);
            SaveOrderFile(doc);
            if (newOrder.IsOrdered) //update manager order:
            {
                AddOrderToManagerOrder(newOrder);
            }
            return UserTodayOrder(newOrder.Account.Id);
        }
        public Order OrderUserTodayOrder(string accountId)
        {
            var order = UserTodayOrder(accountId);
            if (order == null || order.IsBooked /*|| order.IsOrdered*/)
            {
                return order;
            }
            order.IsOrdered = true;
            AddOrderToManagerOrder(order);
            return UpdateUserTodayOrder(order);
        }
        public void DeleteUserTodayOrder(string accountId)
        {
            var order = UserTodayOrder(accountId);
            if (order == null || order.IsBooked)
            {
                return;
            }
            XDocument doc = LoadOrderFile();
            var xmlOrder = doc.Root.Elements("Order").SingleOrDefault(o =>
                order.Id.Equals(o.Element("Id").Value));
            if (xmlOrder != null)
            {
                xmlOrder.Remove();
            }
            SaveOrderFile(doc);
            if (order.IsOrdered)
            {
                var managerOrder = ManagerTodayOrder(order.Manager.Id);
                var tempOrder = managerOrder.Orders.SingleOrDefault(o => order.Id.Equals(o.Id));
                managerOrder.Orders.Remove(tempOrder);
                UpdateManagerTodayOrder(managerOrder);
            }
        }
        #endregion

        #region Manager's Order

        #region private
        static string ManagerOrderFolderPath = Path.Combine(Utils.Utils.StorageLocation(), "ManagerOrders");
        static string ManagerOrderFilePath = Path.Combine(ManagerOrderFolderPath, "ManagerOrders.xml");

        private static object _managerOrderLocker = new object();

        private static XDocument LoadManagerOrderFile()
        {
            return Utils.Utils.InitXmlFile(ManagerOrderFilePath);
        }
        private static void SaveManagerOrderFile(XDocument doc)
        {
            Utils.Utils.SaveFile(_managerOrderLocker, doc, ManagerOrderFilePath);
        }

        #endregion

        public ManagerOrder ManagerTodayOrder(string managerId)
        {
            var doc = LoadManagerOrderFile();
            var xmlOrder = doc.Root.Elements("ManagerOrder").SingleOrDefault(m =>
               managerId.Equals(m.Element("Manager").Element("Id").Value)
                && (DateTime.Now.Date == ((DateTime)m.Element("CreatedTime")).Date));
            return ModelSerializer.Deserialize<ManagerOrder>(xmlOrder);
        }
        public List<Order> TodayOrdersInManagerOrder(string managerId)
        {
            var doc = LoadManagerOrderFile();
            var xmlManagerOrder = doc.Root.Elements("ManagerOrder").SingleOrDefault(o =>
                DateTime.Now.Date == ((DateTime)o.Element("CreatedTime")).Date
                && managerId.Equals(o.Element("Manager").Element("Id").Value));
            var managerOrder = ModelSerializer.Deserialize<ManagerOrder>(xmlManagerOrder);
            if (managerOrder == null || managerOrder.Orders == null || managerOrder.Orders.Count == 0)
            {
                return null;
            }
            return managerOrder.Orders;
        }
        public ManagerOrder BookManagerOrder(string managerId)
        {
            var todayOrder = ManagerTodayOrder(managerId);
            if (todayOrder == null)
            {
                return null;
            }
            if (todayOrder.IsBooked)
            {
                return todayOrder;
            }
            todayOrder.IsBooked = true;
            return UpdateManagerTodayOrder(todayOrder);
        }
        public ManagerOrder AddOrderToManagerOrder(Order newOrder)
        {
            var order = newOrder;
            var managerOrder = ManagerTodayOrder(newOrder.Manager.Id);
            if (managerOrder == null) //new manager Order:
            {
                string newId = Guid.NewGuid().ToString().ToLower();
                order.ManagerOrderId = newId;
                managerOrder = new ManagerOrder
                {
                    Id = newId,
                    Manager = newOrder.Manager,
                    CreatedTime = DateTime.Now,
                    IsBooked = false,
                    Orders = new List<Order> { order },
                };
            }
            else // add new Order to manager Order:
            {
                //already exists:
                var tempOrder = managerOrder.Orders.SingleOrDefault(o => o.Id.Equals(order.Id));
                if (tempOrder != null)
                {
                    managerOrder.Orders.Remove(tempOrder);
                }
                order.ManagerOrderId = managerOrder.Id;
                managerOrder.Orders.Add(order);
            }
            return UpdateManagerTodayOrder(managerOrder);
        }

        #region private method
        private ManagerOrder UpdateManagerTodayOrder(ManagerOrder managerOrder)
        {
            var doc = LoadManagerOrderFile();
            var xmlManagerOrder = doc.Root.Elements("ManagerOrder").SingleOrDefault(o =>
                managerOrder.Id.Equals(o.Element("Id").Value));
            if (xmlManagerOrder != null)
            {
                xmlManagerOrder.Remove();
            }
            if (managerOrder.IsBooked)
            {
                managerOrder.Orders = BookUserOrders(managerOrder.Id);
                CoreService.AccountService.PurchaseBookedOrders(managerOrder.Orders);
            }
            managerOrder.LastModifyTime = DateTime.Now;
            xmlManagerOrder = ModelSerializer.Serialize(managerOrder);
            var mealsWithSum = xmlManagerOrder.Element("MealsWithSum");
            if (mealsWithSum != null)
            {
                mealsWithSum.Remove();
            }
            doc.Root.Add(xmlManagerOrder);
            SaveManagerOrderFile(doc);
            return ManagerTodayOrder(managerOrder.Manager.Id);
        }
        private List<Order> BookUserOrders(string managerOrderId)
        {
            var doc = LoadOrderFile();
            var xmlTodayOrders = doc.Root.Elements("Order").Where(o =>
                DateTime.Now.Date == ((DateTime)o.Element("CreatedTime")).Date
                && managerOrderId.Equals(o.Element("ManagerOrderId").Value));
            foreach (var xmlTodayOrder in xmlTodayOrders)
            {
                xmlTodayOrder.SetElementValue("IsBooked", true);
                xmlTodayOrder.SetElementValue("BookedTime", DateTime.Now);
            }
            SaveOrderFile(doc);
            return ModelSerializer.Deserialize<Order>(xmlTodayOrders);
        }
        #endregion

        #endregion
    }
}
